package name.koppernickus.lsp.analyzer;

import com.google.common.base.Predicate;
import com.google.common.collect.Sets;

import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.Set;

import static com.google.common.collect.Sets.filter;

public class Analyzer {

    private final MetaClassPath metaClassPath;

    public Analyzer(String classpath) {
        this.metaClassPath = new MetaClassPath(classpath);
    }

    public Set<Subclassing> findAllSubclassings() throws ClassNotFoundException {
        Set<Subclassing> result = new HashSet<Subclassing>();
        for (Class aClass : metaClassPath.loadAllClasses()) {
            Class superClass = aClass.getSuperclass();
            if(superClass != null) {
              result.add(new Subclassing(aClass));
            }
        }
        return result;
    }

    public Set<Subclassing> findAllProperSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isProperSubclassing(); }
        });
    }

    public Set<Subclassing> findAllAbstractSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isAbstractSubclassing(); }
        });
    }

    public Set<Subclassing> findAllConcreteSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isConcreteSubclassing(); }
        });
    }

    public Set<Subclassing> findAllExceptionsSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isExceptionsSubclassing(); }
        });
    }

    public Set<Subclassing> findAllOverridingSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isOverridingSubclassing(); }
        });
    }

    public Set<Subclassing> findAllProperOverridingSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) { return s.isProperOverridingSubclassing(); }
        });
    }

    public Set<Subclassing> findAllInterestingSubclassings() throws ClassNotFoundException {
        return filter(findAllSubclassings(), new Predicate<Subclassing>() {
            public boolean apply(Subclassing s) {
                return s.isConcreteSubclassing() && !s.isExceptionsSubclassing() && s.isProperOverridingSubclassing();
            }
        });
    }

    public void printStats() throws ClassNotFoundException {
        Set<Subclassing> all = findAllSubclassings();
        Set<Subclassing> proper = findAllProperSubclassings();
        Set<Subclassing> abstractSubclassings = findAllAbstractSubclassings();
        Set<Subclassing> concrete = findAllConcreteSubclassings();
        Set<Subclassing> exceptions = findAllExceptionsSubclassings();
        Set<Subclassing> overriding = findAllOverridingSubclassings();
        Set<Subclassing> properOverriding = findAllProperOverridingSubclassings();
        Set<Subclassing> interesting = findAllInterestingSubclassings();
        int total = all.size();
        System.out.println("All: " + total);
        System.out.println("Proper: " + percent(proper.size(), total));
        System.out.println("Abstract: " + percent(abstractSubclassings.size(), total));
        System.out.println("Concrete: " + percent(concrete.size(), total));
        System.out.println("Exceptions: " + percent(exceptions.size(), total));
        System.out.println("Overriding: " + percent(overriding.size(), total));
        System.out.println("Proper Overriding: " + percent(properOverriding.size(), total));
        System.out.println("Interesting: " + percent(interesting.size(), total));
    }

    private String percent(int count, int total) {
        double p = (100.0*count)/total;
        return new DecimalFormat("#.##").format(p);
    }

}
